home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / AOCE Sample Code / PowerTalk Access Modules / Sample SMSAM / SampleSMSAM Source / TupleDatabase / DiskLog.cp < prev    next >
Encoding:
Text File  |  1995-07-28  |  28.9 KB  |  1,086 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        DiskLog.cp
  3.  
  4.     Copyright:    © 1991-1994 by Apple Computer, Inc.
  5.                 All rights reserved.
  6.  
  7.     Part of the AOCE Sample SMSAM Package.  Consult the license
  8.     which came with this software for your specific legal rights.
  9.  
  10. */
  11.  
  12.  
  13.  
  14. #ifndef    __DISKLOG__
  15. #include "DiskLog.h"
  16. #endif
  17.  
  18. #ifndef    __STDIO__
  19. #include "StdIO.h"
  20. #endif
  21.  
  22. #ifndef    __IOSTREAM__
  23. #include "IOStream.h"
  24. #endif
  25.  
  26. #ifndef    __DEBUGASSERT__
  27. #include "DebugAssert.h"
  28. #endif
  29.  
  30. #ifndef    __DEBUGGINGGEAR__
  31. #include "DebuggingGear.h"
  32. #endif
  33.  
  34. #ifndef __ABSTRACTFILE__
  35. #include "AbstractFile.h"
  36. #endif
  37.  
  38. #ifndef    __CREATEOBJECT__
  39. #include "CreateObject.h"
  40. #endif
  41.  
  42. #ifndef    __FILESPEC__
  43. #include "FileSpec.h"
  44. #endif
  45.  
  46. #ifndef    __HANDLEOBJECTOWNER__
  47. #include "HandleObjectOwner.h"
  48. #endif
  49.  
  50. #ifndef    __BUFFER__
  51. #include "Buffer.h"
  52. #endif
  53.  
  54. #ifndef    __FILES__
  55. #include "Files.h"
  56. #endif
  57.  
  58. #pragma segment DiskLog
  59.  
  60. #undef    POLYMORPHIC_DISK_LOGGING    // define this to log different types of
  61.                                     // TLogEntries to the same log. This option
  62.                                     // requires application post-compile processing
  63.                                     // using a special MPW tool.
  64.  
  65. /***********************************|****************************************/
  66.  
  67. Boolean
  68. TDiskLog::WriteEntryReconstructInfoAtCurrentMark ( const TLogEntry& entry )
  69. {
  70. #if defined ( POLYMORPHIC_DISK_LOGGING )
  71.     return WritePolymorphicEntryReconstructInfoAtCurrentMark ( entry );
  72. #else
  73.     return true;    // do nothing since we don’t need object reconstruction info
  74. #endif
  75. }
  76.  
  77. /***********************************|****************************************/
  78.  
  79. TLogEntry*
  80. TDiskLog::ReconstructEntryFromInfoAtCurrentMark ()
  81. {
  82. #if defined ( POLYMORPHIC_DISK_LOGGING )
  83.     return ReconstructPolymorphicEntryFromInfoAtCurrentMark ();
  84. #else
  85.     return new TTestEntry ();    // create a new object of the proper type.
  86. #endif
  87. }
  88.  
  89. /***********************************|****************************************/
  90. /***********************************|****************************************/
  91.  
  92. #if defined ( debug ) || defined ( DEBUG )
  93.  
  94. #define    LOG_ASSERT(EXPR) \
  95.         { if ( !AssertDebug (EXPR,#EXPR,__FILE__,__LINE__) ) { chris << *this << endl; return 0; } }
  96.  
  97. #else
  98.  
  99. #define    LOG_ASSERT(EXPR)\
  100.         if ( !(EXPR) ) { return false; }
  101.  
  102. #endif
  103.  
  104. /***********************************|****************************************/
  105.  
  106. typedef unsigned short SynchMarker;
  107.  
  108. const EntryIndex TDiskLog::kInvalidIndex = (EntryIndex) 0;
  109. const EntryIndex kPreEntryOverhead = sizeof ( SynchMarker ) + 2 * sizeof ( EntryIndex );
  110. const EntryIndex kPostEntryOverhead = sizeof ( SynchMarker );
  111. const EntryIndex kTotalEntryOverhead = kPreEntryOverhead + kPostEntryOverhead;
  112. const SynchMarker kSynchStart = ( (SynchMarker) '\n' << 8 ) | (SynchMarker) '{';
  113. const SynchMarker kSynchStop = (SynchMarker) '}#';
  114. const unsigned long kMinimumEntryLength = kTotalEntryOverhead;
  115. const unsigned long kMaximumEntryLength = 0xFFFF;
  116.  
  117. extern void Busy ( short );
  118. inline long Abs ( long a ) { return a < 0 ? -a : a; }
  119. inline Boolean IsValidEntryLength ( unsigned long supposedLength ) { return ( supposedLength >= kMinimumEntryLength ) && ( supposedLength <= kMaximumEntryLength ); }
  120.  
  121. /***********************************|****************************************/
  122. /***********************************|****************************************/
  123.  
  124. TDiskLog::TDiskLog ( const TFileSpec& spec, TPurgeCriteria* criteria ):
  125.     fFile ( new TForkFile ( spec, TAbstractFile::kData, GetLogFileCreator (), GetLogFileType () ) ),
  126.     fCriteria ( criteria ),
  127.     fPermission ( kNoneOfTheAbove )
  128. {
  129.     InitializeHeaderValues ( fHeader );
  130. }
  131.  
  132. /***********************************|****************************************/
  133.  
  134. TDiskLog::~TDiskLog ()
  135. {
  136.     delete fFile;
  137.     delete fCriteria;
  138. }
  139.  
  140. /***********************************|****************************************/
  141.  
  142. Boolean
  143. TDiskLog::Open ( LogPermission permission )
  144. {
  145.     LOG_ASSERT ( kNoneOfTheAbove == fPermission );
  146.     LOG_ASSERT ( EnsureUsabilityOnceOpen ( permission ) );
  147.  
  148.     long end = 0;
  149.     LOG_ASSERT ( noErr == fFile->GetEnd ( end ) );
  150.  
  151.     if ( permission == kRewrite || end < sizeof ( fHeader ) )
  152.     {
  153.         InitializeHeaderValues ( fHeader );
  154.         LOG_ASSERT ( noErr == fFile->SetEnd ( 0 ) );
  155.         LOG_ASSERT ( DumpHeaderInfo () );
  156.     }
  157.     else
  158.     {
  159.         LOG_ASSERT ( LoadHeaderInfo () );
  160.     }
  161.  
  162.     LOG_ASSERT ( noErr == fFile->SetPosition ( sizeof ( fHeader ) ) );
  163.  
  164.     fPermission = permission;
  165.     return true;
  166. }
  167.  
  168. /***********************************|****************************************/
  169.  
  170. Boolean
  171. TDiskLog::Close ()
  172. {
  173.     Boolean success = true;
  174.  
  175.     LOG_ASSERT ( kNoneOfTheAbove != fPermission );
  176.  
  177.     if ( fPermission != kRead )
  178.         ASSERT ( success = DumpHeaderInfo () );
  179.  
  180.     fPermission = kNoneOfTheAbove;
  181.     InitializeHeaderValues ( fHeader );
  182.  
  183.     return success;
  184. }
  185.  
  186. /***********************************|****************************************/
  187.  
  188. Boolean
  189. TDiskLog::EnsureUsabilityOnceOpen ( LogPermission /* permission */ )
  190. {
  191. // seek to the beginning
  192.     LOG_ASSERT ( noErr == fFile->SetPosition ( 0 ) );
  193.  
  194. // seek to the end
  195.     LOG_ASSERT ( noErr == fFile->SetPosition ( fsFromLEOF, 0 ) );
  196.  
  197.     return true;
  198. }
  199.  
  200. /***********************************|****************************************/
  201.  
  202. Boolean
  203. TDiskLog::Delete ()
  204. {
  205. //     LOG_ASSERT ( noErr == fFile->Delete () );
  206.     return true;
  207. }
  208.  
  209. /***********************************|****************************************/
  210.  
  211. Boolean
  212. TDiskLog::AddEntry ( TLogEntry& entry )
  213. {
  214.     EntryID proposed = MapToID ( fHeader.fEntryCount ) + 1, previous = entry.fID;
  215.     entry.fID = proposed;
  216.  
  217.     Boolean success = WriteNewEntry ( entry );
  218.  
  219.     if ( success )
  220.     {
  221.         if ( fCriteria )
  222.             RemoveUntil ( fCriteria->AdviseRemoveUntil ( *this ) );
  223.     }
  224.     else
  225.         entry.fID = previous;
  226.  
  227.     return success;
  228. }
  229.  
  230. /***********************************|****************************************/
  231.  
  232. Boolean
  233. TDiskLog::RemoveUntil ( EntryIndex /* index */ )
  234. {
  235.     NOT_IMPLEMENTED ();
  236.     return false;
  237. }
  238.  
  239. /***********************************|****************************************/
  240.  
  241. void
  242. TDiskLog::AdoptPurgeCriteria ( TPurgeCriteria* criteria )
  243. {
  244.     delete fCriteria;
  245.     fCriteria = criteria;
  246. }
  247.  
  248. /***********************************|****************************************/
  249.  
  250. TPurgeCriteria*
  251. TDiskLog::OrphanPurgeCriteria ()
  252. {
  253.     TPurgeCriteria* criteria = fCriteria;
  254.     fCriteria = nil;
  255.     return criteria;
  256. }
  257.  
  258. /***********************************|****************************************/
  259.  
  260. Boolean
  261. TDiskLog::WriteNewEntry ( TLogEntry& entry )
  262. {
  263.     LOG_ASSERT ( noErr == fFile->SetPosition ( fHeader.fNewEntryOffset ) );
  264.     LOG_ASSERT ( FlattenFromCurrentMark ( entry ) );
  265.     fHeader.fLastEntryOffset = fHeader.fNewEntryOffset;
  266.     LOG_ASSERT ( noErr == fFile->GetPosition ( fHeader.fNewEntryOffset ) );
  267.     fHeader.fEntryCount++;
  268.     LOG_ASSERT ( DumpHeaderInfo () );
  269. //     LOG_ASSERT ( noErr == fFile->Flush () );
  270.     return true;
  271. }
  272.  
  273. /***********************************|****************************************/
  274.  
  275. TLogEntry*
  276. TDiskLog::CreateEntry ( EntryIndex index )
  277. {
  278.     if ( ASSERT ( IsValidIndex ( index ) ) )
  279.         if ( ASSERT ( MoveToValidEntry ( index ) ) )
  280.             return ResurrectFromCurrentMark ();
  281.  
  282.     return nil;
  283. }
  284.  
  285. /***********************************|****************************************/
  286.  
  287. Boolean
  288. TDiskLog::MoveToValidEntry ( EntryIndex index )
  289. {
  290.     if ( !IsValidIndex ( fHeader.fCurrentEntryIndex ) )
  291.     {
  292.         fHeader.fCurrentEntryIndex = 1;
  293.         fHeader.fCurrentEntryOffset = sizeof ( fHeader );
  294.     }
  295.  
  296.     long fromFirstEntryJumps = (long) index - (long) 1;
  297.     long fromLastEntryJumps = (long) index - (long) fHeader.fEntryCount;
  298.     long fromCurrentEntryJumps = (long) index - (long) fHeader.fCurrentEntryIndex;
  299.     long bestJump; EntryIndex bestIndex; EntryOffset bestOffset;
  300.  
  301.     // there are three entry positions in the file which we directly
  302.     // know how to get to (first, current, & last).
  303.     // this routine computes which of the three is closest to the
  304.     // the desired index in terms of the number of jumps required.
  305.     // once we know which of the three is closest, we start jumping
  306.     // to the entry index which the user has requested.
  307.  
  308.     if ( Abs ( fromFirstEntryJumps ) <= Abs ( fromLastEntryJumps ) )
  309.     {
  310.         bestJump = fromFirstEntryJumps;
  311.         bestOffset = sizeof ( fHeader );
  312.         bestIndex = 1;
  313.     }
  314.     else
  315.     {
  316.         bestJump = fromLastEntryJumps;
  317.         bestOffset = fHeader.fLastEntryOffset;
  318.         bestIndex = fHeader.fEntryCount;
  319.     }
  320.  
  321.     if ( Abs ( fromCurrentEntryJumps ) < Abs ( bestJump ) )
  322.     {
  323.         bestJump = fromCurrentEntryJumps;
  324.         bestOffset = fHeader.fCurrentEntryOffset;
  325.         bestIndex = fHeader.fCurrentEntryIndex;
  326.     }
  327.  
  328.     LOG_ASSERT ( noErr == fFile->SetPosition ( bestOffset ) );
  329.     fHeader.fCurrentEntryOffset = bestOffset;
  330.     fHeader.fCurrentEntryIndex = bestIndex;
  331.  
  332.     while ( bestJump > 0 )
  333.     {
  334.         LOG_ASSERT ( MoveToNextEntry () );
  335.         bestJump--;
  336.     }
  337.  
  338.     while ( bestJump < 0 )
  339.     {
  340.         LOG_ASSERT ( MoveToPreviousEntry () );
  341.         bestJump++;
  342.     }
  343.  
  344.     return true;
  345. }
  346.  
  347. /***********************************|****************************************/
  348.  
  349. Boolean
  350. TDiskLog::MoveToNextEntry ()
  351. {
  352.     Busy ( 10 );
  353.  
  354.     // the mark should already be at the first byte of the current entry
  355.  
  356.     // skip over synch marker and previous offset info
  357.     LOG_ASSERT ( noErr == fFile->MovePosition ( sizeof ( SynchMarker ) ) );
  358.  
  359.     // read the entry length
  360.     EntryOffset entryLength = 0;
  361.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( &entryLength, sizeof ( entryLength ) ) );
  362.  
  363.     // move to the next entry start
  364.     EntryOffset offset = fHeader.fCurrentEntryOffset + entryLength;
  365.     LOG_ASSERT ( noErr == fFile->SetPosition ( offset ) );
  366.     fHeader.fCurrentEntryOffset = offset;
  367.     ++fHeader.fCurrentEntryIndex;
  368.  
  369.     return true;
  370. }
  371.  
  372. /***********************************|****************************************/
  373.  
  374. Boolean
  375. TDiskLog::MoveToPreviousEntry ()
  376. {
  377.     Busy ( 10 );
  378.  
  379.     // the mark should already be at the first byte of the current entry
  380.  
  381.     // skip past the entry synch marker and entry length fields
  382.     LOG_ASSERT ( noErr == fFile->MovePosition ( sizeof ( SynchMarker ) + sizeof ( EntryOffset ) ) );
  383.  
  384.     // read the offset to the start of the previous entry
  385.     EntryOffset entryOffset = 0;
  386.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( &entryOffset, sizeof ( entryOffset ) ) );
  387.  
  388.     if ( entryOffset != 0 )
  389.     {
  390.         // attempt to move to the start of the previous entry using a negative offset
  391.         LOG_ASSERT ( noErr == fFile->SetPosition ( fHeader.fCurrentEntryOffset + entryOffset ) );
  392.         fHeader.fCurrentEntryOffset += entryOffset;
  393.         --fHeader.fCurrentEntryIndex;
  394.         return true;
  395.     }
  396.     else
  397.     {
  398.         // we are at the first entry
  399.         return false;
  400.     }
  401. }
  402.  
  403. /***********************************|****************************************/
  404.  
  405. TLogEntry*
  406. TDiskLog::ScavengeFromMark ()
  407. {
  408.     NOT_IMPLEMENTED ();
  409.     return nil;
  410. }
  411.  
  412. /***********************************|****************************************/
  413.  
  414. FormatVersion
  415. TDiskLog::GetFormatVersion () const
  416. {
  417.     return ( ( kMinimumEntryLength ) << ( sizeof ( FormatVersion ) / 2 * 8 ) ) | sizeof ( LogHeader );
  418. }
  419.  
  420. /***********************************|****************************************/
  421.  
  422. void
  423. TDiskLog::InitializeHeaderValues ( LogHeader& header ) const
  424. {
  425.     header.fFormatVersion = GetFormatVersion ();
  426.     header.fEntryCount = 0;
  427.     header.fFirstEntryID = 1;
  428.     header.fNewEntryOffset = header.fLastEntryOffset = sizeof ( fHeader );
  429.     header.fCurrentEntryIndex = 1;
  430.     header.fCurrentEntryOffset = sizeof ( fHeader );
  431. }
  432.  
  433. /***********************************|****************************************/
  434.  
  435. Boolean
  436. TDiskLog::HandleError ( OSErr, const char* /* file */, unsigned long /* line */ )
  437. {
  438.     NOT_IMPLEMENTED ();
  439.     return false;
  440. }
  441.  
  442. /***********************************|****************************************/
  443.  
  444. Boolean
  445. TDiskLog::LoadHeaderInfo ()
  446. {
  447.     long endLength = 0;
  448.     LOG_ASSERT ( noErr == fFile->GetEnd ( endLength ) );
  449.  
  450.     if ( !ASSERT ( endLength >= sizeof ( fHeader ) ) )
  451.     {
  452.         InitializeHeaderValues ( fHeader );
  453.         return true;     // we have a new virgin file
  454.     }
  455.  
  456.     if ( !ASSERT ( noErr == fFile->SetPosition ( 0 ) ) )
  457.     {
  458.         InitializeHeaderValues ( fHeader );
  459.         return false;     // we cannot set the mark to the beginning
  460.     }
  461.  
  462.     if ( !ASSERT ( noErr == fFile->ReadDataIgnore ( &fHeader, sizeof ( fHeader ) ) ) )
  463.     {
  464.         InitializeHeaderValues ( fHeader );
  465.         return false;    // we could not read the header info
  466.     }
  467.  
  468.     if ( !ASSERT ( fHeader.fFormatVersion == GetFormatVersion () ) )
  469.     {
  470.         if ( !HandleUnexpectedVersion ( fHeader.fFormatVersion ) )
  471.         {
  472.             InitializeHeaderValues ( fHeader );
  473.             return false; // we could not handle an unexpected version
  474.         }
  475.     }
  476.  
  477.     if ( !ASSERT ( ValidateHeaderContents ( fHeader, endLength ) ) )
  478.     {
  479.         InitializeHeaderValues ( fHeader );
  480.         return false;    // we have possibly corrupt header info
  481.     }
  482.  
  483.     return true;
  484. }
  485.  
  486. /***********************************|****************************************/
  487.  
  488. Boolean
  489. TDiskLog::HandleUnexpectedVersion ( FormatVersion /* version */ )
  490. {
  491.     NOT_IMPLEMENTED ();
  492.     return false;
  493. }
  494.  
  495. /***********************************|****************************************/
  496.  
  497. OSType
  498. TDiskLog::GetLogFileCreator () const
  499. {
  500.     return 'MPS ';
  501. }
  502.  
  503. /***********************************|****************************************/
  504.  
  505. OSType
  506. TDiskLog::GetLogFileType () const
  507. {
  508.     return 'TEXT';
  509. }
  510.  
  511. /***********************************|****************************************/
  512.  
  513. Boolean
  514. TDiskLog::ValidateHeaderContents ( const LogHeader& header, unsigned long fileEnd ) const
  515. {
  516.     LOG_ASSERT ( fileEnd >= ( sizeof ( LogHeader ) + kMinimumEntryLength * header.fEntryCount ) );
  517.     LOG_ASSERT ( sizeof ( fHeader ) <= fileEnd );
  518.     LOG_ASSERT ( header.fNewEntryOffset <= fileEnd );
  519.     LOG_ASSERT ( header.fCurrentEntryOffset <= fileEnd );
  520.     return true;
  521. }
  522.  
  523. /***********************************|****************************************/
  524.  
  525. Boolean
  526. TDiskLog::DumpHeaderInfo ()
  527. {
  528.     LOG_ASSERT ( noErr == fFile->SetPosition ( 0 ) );
  529.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &fHeader, sizeof ( fHeader ) ) );
  530.     return true;
  531. }
  532.  
  533. /***********************************|****************************************
  534.  
  535.     Each log entry has the following variable length file format:
  536.  
  537.        Description                                  Size         Offset
  538.     ------------------------------------------   ----------   ------------
  539.     #1. start entry synch marker                      M         0
  540.     #2. total entry data length (incl. syncs)         E         M+E
  541.     #3. offset to prev entry start                    E         M
  542.     #4. class defined construct info                  C         M+2*E
  543.     #5. log entry data                                D         M+2*E+C
  544.     #6. stop entry synch marker                       M         M+2*E+C+D
  545.  
  546.     Notes:
  547.  
  548.         M = sizeof ( SynchMarker ).
  549.         E = sizeof ( EntryOffset )
  550.         C = depends on the override of WriteEntryConstruct…/ConstructEntry…
  551.         D >= sizeof ( TLogEntry ), depends on what the entry actually writes
  552.  
  553. ***********************************|****************************************/
  554.  
  555. Boolean
  556. TDiskLog::FlattenFromCurrentMark ( TLogEntry& entry )
  557. {
  558. // get position of the start of the entry
  559.     EntryOffset startPosition = 0;
  560.     LOG_ASSERT ( noErr == fFile->GetPosition ( startPosition ) );
  561.  
  562. // write a start synch marker
  563.     SynchMarker marker = kSynchStart;
  564.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &marker, sizeof ( marker ) ) );
  565.  
  566. // write zero for length until we determine what the actual length is after writing the entry
  567.     EntryOffset zeroValue = 0;
  568.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &zeroValue, sizeof ( zeroValue ) ) );
  569.  
  570. // since we know the offset of the previous entry, we can write it now
  571.     EntryOffset previousEntryOffset = fHeader.fLastEntryOffset - startPosition;
  572.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &previousEntryOffset, sizeof ( previousEntryOffset ) ) );
  573.  
  574. // write the entry class information for later re-construction
  575.     LOG_ASSERT ( WriteEntryReconstructInfoAtCurrentMark ( entry ) );
  576.  
  577. //     write the entry data by asking the entry to write itself
  578.     LOG_ASSERT ( entry.WriteTo ( *fFile ) );
  579.  
  580. // write a stop synch marker
  581.     marker = kSynchStop;
  582.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &marker, sizeof ( marker ) ) );
  583.  
  584. // get position after writing entry data
  585.     EntryOffset stopPosition = 0;
  586.     LOG_ASSERT ( noErr == fFile->GetPosition ( stopPosition ) );
  587.  
  588. // determine our entry’s length and check for size validity
  589.     unsigned long entryLength = stopPosition - startPosition;
  590.     LOG_ASSERT ( IsValidEntryLength ( entryLength ) );
  591.  
  592. // move to after the start synch marker and prev offset
  593.     LOG_ASSERT ( noErr == fFile->SetPosition ( startPosition + sizeof ( SynchMarker ) ) );
  594.  
  595. // write the entry length
  596.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &entryLength, sizeof ( entryLength ) ) );
  597.  
  598. // return the position to the end of the entry data
  599.     LOG_ASSERT ( noErr == fFile->SetPosition ( stopPosition ) );
  600.  
  601.     return true;
  602. }
  603.  
  604. /***********************************|****************************************/
  605.  
  606. TLogEntry*
  607. TDiskLog::ResurrectFromCurrentMark ()
  608. {
  609.     TLogEntry* entry = nil;
  610.  
  611. // remember the position at the start of tbe entry data
  612.     long startPosition = 0;
  613.     LOG_ASSERT ( noErr == fFile->GetPosition ( startPosition ) );
  614.  
  615. // read a synchronization marker
  616.     SynchMarker marker = ~kSynchStart;    // cute initial value, huh?
  617.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( &marker, sizeof ( marker ) ) );
  618.  
  619. // did we read a synch marker?
  620.     LOG_ASSERT ( kSynchStart == marker );
  621.  
  622. // read the entry length
  623.     unsigned long expectedEntryLength = 0;
  624.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( &expectedEntryLength, sizeof ( expectedEntryLength ) ) );
  625.  
  626. // verify expectedEntryLength
  627.     LOG_ASSERT ( IsValidEntryLength ( expectedEntryLength ) );
  628.  
  629. // skip past the prev offset info
  630.     LOG_ASSERT ( noErr == fFile->MovePosition ( sizeof ( EntryOffset ) ) );
  631.  
  632. // construct entry from info
  633.     entry = ReconstructEntryFromInfoAtCurrentMark ();
  634.  
  635.     if ( entry != nil )
  636.     {
  637.     //     read the entry data
  638.         if ( !entry->ReadFrom ( *fFile ) )
  639.         {
  640.         // delete the reconstructed entry
  641.             delete entry;
  642.             return false;
  643.         }
  644.     }
  645.     else
  646.     {
  647.     // advance past the entry data
  648.         LOG_ASSERT ( noErr == fFile->SetPosition ( startPosition + expectedEntryLength - kPreEntryOverhead ) );
  649.     }
  650.  
  651. // read a synchronization marker
  652.     marker = ~kSynchStop;    // cute initial value, huh?
  653.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( &marker, sizeof ( marker ) ) );
  654.  
  655. // did we read a synch marker?
  656.     LOG_ASSERT ( kSynchStop == marker );
  657.  
  658. // remember the position at the end of tbe entry data
  659.     long stopPosition = 0;
  660.     LOG_ASSERT ( noErr == fFile->GetPosition ( stopPosition ) );
  661.  
  662. // ensure the expected and actual entry lengths are the same
  663.     const unsigned long actualEntryLength = stopPosition - startPosition;
  664.     LOG_ASSERT ( actualEntryLength == expectedEntryLength );
  665.  
  666.     return entry;
  667. }
  668.  
  669. /***********************************|****************************************/
  670.  
  671. const unsigned long kMinimumClassNameLength = 1;
  672. const unsigned long kMaximumClassNameLength = 128;
  673.  
  674. inline Boolean
  675. IsValidClassNameLength ( unsigned long supposedLength )
  676. {
  677.      return ( supposedLength >= kMinimumClassNameLength ) && ( supposedLength <= kMaximumClassNameLength );
  678. }
  679.  
  680. /***********************************|****************************************/
  681.  
  682. Boolean
  683. TDiskLog::WritePolymorphicEntryReconstructInfoAtCurrentMark ( const TLogEntry& entry )
  684. {
  685. // write the entry class name length and string data
  686.     const char* entryClass = entry.GetClass ();
  687.     unsigned short length = strlen ( entryClass );
  688.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( &length, sizeof ( length ) ) );
  689.     LOG_ASSERT ( noErr == fFile->WriteDataIgnore ( entryClass, length ) );
  690.     return true;
  691. }
  692.  
  693. /***********************************|****************************************/
  694.  
  695. TLogEntry*
  696. TDiskLog::ReconstructPolymorphicEntryFromInfoAtCurrentMark ()
  697. {
  698. // read the class name length
  699.     unsigned short classNameLength = 0;
  700.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( &classNameLength, sizeof ( classNameLength ) ) );
  701.  
  702. // is the assumed name length valid?
  703.     LOG_ASSERT ( IsValidClassNameLength ( classNameLength ) );
  704.  
  705. // read the entry class string
  706.     char className [ kMaximumClassNameLength + 1 ];
  707.     LOG_ASSERT ( noErr == fFile->ReadDataIgnore ( className, classNameLength ) );
  708.     className [ classNameLength ] = 0;
  709.  
  710. // create an empty object
  711.     TLogEntry* entry = (TLogEntry*) CreateObject ( className );
  712.  
  713.     if ( entry == nil )
  714.     {
  715.         cerr << "FILE " << __FILE__ << "; LINE " << __LINE__ << "; # Could not reconstruct \"" << className << "\"\n";
  716.         cerr.flush ();
  717.     }
  718.  
  719.     return entry;
  720. }
  721.  
  722. /***********************************|****************************************/
  723.  
  724. Boolean
  725. TDiskLog::ValidateDiskContents ( ostream& )
  726. {
  727.     NOT_IMPLEMENTED ();
  728.     return false;
  729. }
  730.  
  731. /***********************************|****************************************/
  732.  
  733. #if debug
  734.  
  735. Boolean
  736. TDiskLog::InvariantCheck ( ostream& out ) const
  737. {
  738. //     NOT_IMPLEMENTED ();
  739. //     *this >> out;    // dump our state for now
  740.     return true;
  741. }
  742.  
  743. /***********************************|****************************************/
  744.  
  745. Boolean
  746. TDiskLogEnumerator::InvariantCheck ( ostream& out ) const
  747. {
  748.     NOT_IMPLEMENTED ();
  749.     *this >> out;    // dump our state for now
  750.     return fLog.InvariantCheck ( out );
  751. }
  752.  
  753. /***********************************|****************************************/
  754.  
  755. Boolean
  756. TDiskLogIterator::InvariantCheck ( ostream& out ) const
  757. {
  758.     return fImplementation.InvariantCheck ( out );
  759. }
  760.  
  761. /***********************************|****************************************/
  762.  
  763. ostream&
  764. TDiskLog::operator >> ( ostream& stream ) const
  765. {
  766.     stream << "TDiskLog:" << (void*) this << "\n";
  767.     stream << "\n\tfFile:" << fFile << endl;
  768.     stream << "\n\tfHeader.fFormatVersion: " << (short) fHeader.fFormatVersion;
  769.     stream << "\n\tfHeader.fEntryCount: " << fHeader.fEntryCount;
  770.     stream << "\n\tfHeader.fFirstEntryID: " << fHeader.fFirstEntryID;
  771.     stream << "\n\tfHeader.fLastEntryOffset: " << fHeader.fLastEntryOffset;
  772.     stream << "\n\tfHeader.fNewEntryOffset: " << fHeader.fNewEntryOffset;
  773.     stream << "\n\tfHeader.fCurrentEntryIndex: " << fHeader.fCurrentEntryIndex;
  774.     stream << "\n\tfHeader.fCurrentEntryOffset: " << fHeader.fCurrentEntryOffset;
  775.     stream << "\n\tfCriteria:" << fCriteria;
  776.     stream << "\n\tfPermission:";
  777.  
  778.     switch ( fPermission )
  779.     {
  780.         case kRewrite:
  781.             stream << "kRewrite\n";
  782.         break;
  783.  
  784.         case kAppend:
  785.             stream << "kAppend\n";
  786.         break;
  787.  
  788.         case kRead:
  789.             stream << "kRead\n";
  790.         break;
  791.  
  792.         case kNoneOfTheAbove:
  793.             stream << "kNoneOfTheAbove\n";
  794.         break;
  795.     }
  796.  
  797.     return stream;
  798. }
  799.  
  800. #endif    // debug
  801.  
  802. /***********************************|****************************************/
  803. /***********************************|****************************************/
  804.  
  805. TDiskLogEnumerator::TDiskLogEnumerator ( TDiskLog& log ):
  806.     fLog ( log ),
  807.     fEntryCache ( fLog.CreateEntry ( 1 ) ),
  808.     fFirstEntryID ( fEntryCache && fLog.IsValidIndex ( 1 )
  809.                         ? fEntryCache->GetID () : TLogEntry::kInvalidID )
  810. {
  811. }
  812.  
  813. /***********************************|****************************************/
  814.  
  815. TDiskLogEnumerator::~TDiskLogEnumerator ()
  816. {
  817.     delete fEntryCache;
  818. }
  819.  
  820. /***********************************|****************************************/
  821.  
  822. const TLogEntry*
  823. TDiskLogEnumerator::GetEntryByIndex ( EntryIndex index )
  824. {
  825.     delete fEntryCache;
  826.     fEntryCache = fLog.CreateEntry ( index );
  827.     return fEntryCache;
  828. }
  829.  
  830. /***********************************|****************************************/
  831.  
  832. ostream&
  833. TDiskLogEnumerator::operator >> ( ostream& stream ) const
  834. {
  835.     stream << "TDiskLogEnumerator:" << (void*) this << "\n";
  836.     stream << "\tfEntryCache:" << fEntryCache << "\n";
  837.     stream << "\tfFirstEntryID:" << fFirstEntryID << "\n";
  838.     fLog >> stream;
  839.     return stream;
  840. }
  841.  
  842. /***********************************|****************************************/
  843. /***********************************|****************************************/
  844.  
  845. TDiskLogIterator::TDiskLogIterator ( TDiskLog& log ):
  846.     fLog ( log ),
  847.     fImplementation ( fLog ),
  848.     fNextIndex ( TDiskLog::kInvalidIndex )
  849. {
  850. }
  851.  
  852. /***********************************|****************************************/
  853.  
  854. TDiskLogIterator::~TDiskLogIterator ()
  855. {
  856. }
  857.  
  858. /***********************************|****************************************/
  859.  
  860. const TLogEntry*
  861. TDiskLogIterator::GetFirstEntry ()
  862. {
  863.     fNextIndex = 1;
  864.     return GetNextEntry ();
  865. }
  866.  
  867. /***********************************|****************************************/
  868.  
  869. const TLogEntry*
  870. TDiskLogIterator::GetNextEntry ()
  871. {
  872.     if ( fLog.IsValidIndex ( fNextIndex ) )
  873.     {
  874.         const TLogEntry* entry = fImplementation.GetEntryByIndex ( fNextIndex );
  875.  
  876.         if ( entry )
  877.             ++fNextIndex;
  878.         else
  879.             fNextIndex = TDiskLog::kInvalidIndex;
  880.  
  881.         return entry;
  882.     }
  883.     else
  884.     {
  885.         return nil;
  886.     }
  887. }
  888.  
  889. /***********************************|****************************************/
  890.  
  891. const TLogEntry*
  892. TDiskLogIterator::GetLastEntry ()
  893. {
  894.     fNextIndex = fImplementation.CountEntries ();
  895.     return GetPreviousEntry ();
  896. }
  897.  
  898. /***********************************|****************************************/
  899.  
  900. const TLogEntry*
  901. TDiskLogIterator::GetPreviousEntry ()
  902. {
  903.     if ( fLog.IsValidIndex ( fNextIndex ) )
  904.     {
  905.         const TLogEntry* entry = fImplementation.GetEntryByIndex ( fNextIndex );
  906.  
  907.         if ( entry )
  908.             --fNextIndex;
  909.         else
  910.             fNextIndex = TDiskLog::kInvalidIndex;
  911.  
  912.         return entry;
  913.     }
  914.     else
  915.     {
  916.         return nil;
  917.     }
  918. }
  919.  
  920. /***********************************|****************************************/
  921.  
  922. TLogEntry*
  923. TDiskLogIterator::AdoptFirstEntry ()
  924. {
  925.     fNextIndex = 1;
  926.     return AdoptNextEntry ();
  927. }
  928.  
  929. /***********************************|****************************************/
  930.  
  931. TLogEntry*
  932. TDiskLogIterator::AdoptNextEntry ()
  933. {
  934.     if ( fLog.IsValidIndex ( fNextIndex ) )
  935.     {
  936.         TLogEntry* entry = fImplementation.AdoptEntryByIndex ( fNextIndex );
  937.  
  938.         if ( entry )
  939.             ++fNextIndex;
  940.         else
  941.             fNextIndex = TDiskLog::kInvalidIndex;
  942.  
  943.         return entry;
  944.     }
  945.     else
  946.     {
  947.         return nil;
  948.     }
  949. }
  950.  
  951. /***********************************|****************************************/
  952.  
  953. TLogEntry*
  954. TDiskLogIterator::AdoptLastEntry ()
  955. {
  956.     fNextIndex = fImplementation.CountEntries ();
  957.     return AdoptPreviousEntry ();
  958. }
  959.  
  960. /***********************************|****************************************/
  961.  
  962. TLogEntry*
  963. TDiskLogIterator::AdoptPreviousEntry ()
  964. {
  965.     if ( fLog.IsValidIndex ( fNextIndex ) )
  966.     {
  967.         TLogEntry* entry = fImplementation.AdoptEntryByIndex ( fNextIndex );
  968.  
  969.         if ( entry )
  970.             --fNextIndex;
  971.         else
  972.             fNextIndex = TDiskLog::kInvalidIndex;
  973.  
  974.         return entry;
  975.     }
  976.     else
  977.     {
  978.         return nil;
  979.     }
  980. }
  981.  
  982. /***********************************|****************************************/
  983.  
  984. ostream&
  985. TDiskLogIterator::operator >> ( ostream& stream ) const
  986. {
  987.     stream << "TDiskLogIterator:" << (void*) this << "\n";
  988.     stream << "fNextIndex:" << fNextIndex << "\n";
  989.     fImplementation >> stream;
  990.     stream.flush ();
  991.     return stream;
  992. }
  993.  
  994. /***********************************|****************************************/
  995. /***********************************|****************************************/
  996.  
  997. EntryIndex
  998. TFileSizePurgeCriteria::AdviseRemoveUntil ( const TDiskLog& log )
  999. {
  1000.     return TDiskLog::kInvalidIndex;
  1001. }
  1002.  
  1003. /***********************************|****************************************/
  1004.  
  1005. EntryIndex
  1006. TEntryCountCriteria::AdviseRemoveUntil ( const TDiskLog& log )
  1007. {
  1008.     return TDiskLog::kInvalidIndex;
  1009. }
  1010.  
  1011. /***********************************|****************************************/
  1012. /***********************************|****************************************/
  1013.  
  1014. extern ostream& DumpHex (ostream& s, const void *p, unsigned long size);
  1015.  
  1016. void TDiskLog::DumpLog ( ostream& stream, const FSSpec& spec )
  1017. {
  1018.     short refNum = -1;
  1019.     ASSERT_RETURN ( noErr == ::FSpOpenDF ( &spec, fsRdPerm, &refNum ) );
  1020.     TAbstractFile* file = new TWrapperFile ( refNum, true );
  1021.     THandleObjectOwner owner ( file );
  1022.     
  1023.     LogHeader header;
  1024.     ASSERT_RETURN ( noErr == file->SetPosition ( 0 ) );
  1025.     ASSERT_RETURN ( noErr == file->ReadDataIgnore ( &header, sizeof ( header ) ) );
  1026.  
  1027.     stream << "fFormatVersion: " << (short) header.fFormatVersion << endl;
  1028.     stream << "fEntryCount: " << header.fEntryCount << endl;
  1029.     stream << "fFirstEntryID: " << header.fFirstEntryID << endl;
  1030.     stream << "fLastEntryOffset: " << header.fLastEntryOffset << endl;
  1031.     stream << "fNewEntryOffset: " << header.fNewEntryOffset << endl;
  1032.     stream << "fCurrentEntryIndex: " << header.fCurrentEntryIndex << endl;
  1033.     stream << "fCurrentEntryOffset: " << header.fCurrentEntryOffset << endl;
  1034.  
  1035.     long position, count = header.fEntryCount;
  1036.     ASSERT_RETURN ( noErr == file->SetPosition ( sizeof ( header ) ) );
  1037.  
  1038.     while ( --count >= 0 )
  1039.     {
  1040.         stream << "\nEntry #" << header.fEntryCount - count << " @ file position " << file->GetPosition () << endl;
  1041.  
  1042.         // try to read a start entry synch marker
  1043.         SynchMarker marker = ~kSynchStart;
  1044.         ASSERT_RETURN ( noErr == file->ReadDataIgnore ( &marker, sizeof ( marker ) ) );
  1045.         ASSERT_RETURN ( kSynchStart == marker );
  1046.         stream << "start synch marker: okay" << endl;
  1047.  
  1048.         // read current entry length
  1049.         long length = 0;
  1050.         ASSERT_RETURN ( noErr == file->Read ( length ) );
  1051.         stream << "total entry length: " << length << endl;
  1052.         length -= kTotalEntryOverhead;
  1053.         stream << "entry data length: " << length << endl;
  1054.  
  1055.         // read the offset to the previous entry
  1056.         ASSERT_RETURN ( noErr == file->Read ( position ) );
  1057.         stream << "previous entry offset: " << position << endl;
  1058.  
  1059.         // read the entry data
  1060.         CBuffer buffer ( length );
  1061.         ASSERT_RETURN ( noErr == file->ReadDataIgnore ( (void*) buffer.GetPhysicalStart (), buffer.GetPhysicalLength () ) );
  1062.         DumpHex ( stream, buffer.GetPhysicalStart (), buffer.GetPhysicalLength () );
  1063.         
  1064.         // try to read a stop entry synch marker
  1065.         marker = ~kSynchStop;
  1066.         ASSERT_RETURN ( noErr == file->ReadDataIgnore ( &marker, sizeof ( marker ) ) );
  1067.         ASSERT_RETURN ( kSynchStop == marker );
  1068.         stream << "stop synch marker: okay" << endl;
  1069.     }
  1070. }
  1071.  
  1072. /***********************************|****************************************/
  1073.  
  1074. void TDiskLog::DumpLog ( ostream& stream )
  1075. {
  1076.     StandardFileReply reply;
  1077.     OSType type[4] = { 'TEXT', '    ', '    ', '    '};
  1078.  
  1079.     ::StandardGetFile ( nil, 1, type, &reply );
  1080.  
  1081.     if ( reply.sfGood )
  1082.         DumpLog ( stream, reply.sfFile );
  1083. }
  1084.  
  1085. /***********************************|****************************************/
  1086.